﻿using Sidvall.IO;
using Sidvall.Serialization;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Sidvall.Web.IO
{
    public class Storage : Sidvall.IO.IStorage
    {
        #region Public Members

        #region Store

        private Store _Store;
        public Store Store
        {
            get
            {
                return _Store;
            }
            set
            {
                _Store = value;
            }
        }

        #endregion
        #region StorageManager

        private StorageManager _StorageManager;
        public virtual StorageManager StorageManager
        {
            get
            {
                if (_StorageManager != null)
                    return _StorageManager;
                return Sidvall.Web.IO.SystemContext.Current.StorageManager;
            }
            set
            {
                _StorageManager = value;
            }
        }

        #endregion
        #region DefaultBufferSize

        public int DefaultBufferSize
        {
            get
            {
                return this.StorageManager.Storage.DefaultBufferSize;
            }
        }

        #endregion
        #region FolderDelimiter

        public string FolderDelimiter
        {
            get
            {
                return "/";
            }
        }

        #endregion
        #region RootPath

        public string RootPath
        {
            get
            {
                if (this.Store == null)
                    return null;
                return this.Store.RootPath;
            }
        }

        #endregion

        #region Combine

        public string Combine(params string[] paths)
        {
            if (paths == null)
                return null;
            var sb = new System.Text.StringBuilder();
            foreach (var path in paths)
            {
                if (sb.Length > 0)
                    sb.Append(this.FolderDelimiter);
                sb.Append(path.Trim(this.FolderDelimiter, System.StringComparison.Ordinal));
            }
            return sb.ToString().Replace(new string[] { "\\", System.IO.Path.DirectorySeparatorChar.ToString(), this.InternalFolderDelimiter }, this.FolderDelimiter, System.StringComparison.Ordinal);
        }

        #endregion
        #region GetFolderPath

        public string GetFolderPath(FolderType folder)
        {
            return this.StorageManager.GetFolderPath(folder);
        }

        #endregion
        #region GetPath

        /// <summary>
        /// Returns the full path for this storage:
        /// [RootPath]/[path]
        /// </summary>
        public virtual string GetPath(string path)
        {
            path = this.StorageManager.GetStoragePath(path);
            if (path != null)
                path = path.Replace(new string[] { "\\", System.IO.Path.DirectorySeparatorChar.ToString(), this.InternalFolderDelimiter }, this.FolderDelimiter, System.StringComparison.Ordinal);
            if (string.IsNullOrWhiteSpace(this.RootPath))
                return path;
            if (string.IsNullOrWhiteSpace(path))
                return this.RootPath;
            return Combine(this.RootPath, path);
        }

        #endregion
        #region GetStoragePath

        /// <summary>
        /// Returns the path that uniquely identifies the item to this storage:
        /// [path]
        /// </summary>
        public virtual string GetStoragePath(string path)
        {
            path = this.StorageManager.GetStoragePath(path);
            if (path != null)
                path = path.Replace(new string[] { "\\", System.IO.Path.DirectorySeparatorChar.ToString(), this.InternalFolderDelimiter }, this.FolderDelimiter, System.StringComparison.Ordinal);
            path = Sidvall.IO.StorageGroupManager.GetPathWithoutRoot(path, this.RootPath, this.FolderDelimiter);
            return path;
        }

        #endregion

        #region CreateFolderAsync

        public virtual async Task<bool> CreateFolderAsync(string path, CancellationToken cancellationToken)
        {
            path = GetPath(path);
            return await this.StorageManager.CreateFolderAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #region DeleteFileAsync

        public virtual async Task<bool> DeleteFileAsync(string path, CancellationToken cancellationToken)
        {
            path = GetPath(path);
            return await this.StorageManager.DeleteFileAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region DeleteFolderAsync

        public async Task<bool> DeleteFolderAsync(string path, bool recursive, CancellationToken cancellationToken)
        {
            path = GetPath(path);
            return await this.StorageManager.DeleteFolderAsync(path, recursive, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #region FileExistsAsync

        public virtual async Task<bool> FileExistsAsync(string path, CancellationToken cancellationToken)
        {
            path = GetPath(path);
            return await this.StorageManager.FileExistsAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region FolderExistsAsync

        public virtual async Task<bool> FolderExistsAsync(string path, CancellationToken cancellationToken)
        {
            path = GetPath(path);
            return await this.StorageManager.FolderExistsAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #region GetFileNamesAsync

        public virtual async Task<IEnumerable<string>> GetFileNamesAsync(string path, string searchPattern, SearchOption searchOption,
            CancellationToken cancellationToken)
        {
            path = GetPath(path);
            var items = await this.StorageManager.GetFileNamesAsync(path, GetSearchPatterns(searchPattern), GetSearchOption(searchOption), cancellationToken).ConfigureAwait(false);
            return GetStoragePaths(items);
        }

        #endregion
        #region GetFolderNamesAsync

        public virtual async Task<IEnumerable<string>> GetFolderNamesAsync(string path, string searchPattern, SearchOption searchOption,
            CancellationToken cancellationToken)
        {
            path = GetPath(path);
            var items = await this.StorageManager.GetFolderNamesAsync(path, GetSearchPatterns(searchPattern), GetSearchOption(searchOption), cancellationToken).ConfigureAwait(false);
            return GetStoragePaths(items);
        }

        #endregion
        #region GetFileSystemEntryNamesAsync

        public virtual async Task<IEnumerable<string>> GetFileSystemEntryNamesAsync(string path, string searchPattern, SearchOption searchOption,
            CancellationToken cancellationToken)
        {
            path = GetPath(path);
            var items = await this.StorageManager.GetFileSystemEntryNamesAsync(path, GetSearchPatterns(searchPattern), GetSearchOption(searchOption), cancellationToken).ConfigureAwait(false);
            return GetStoragePaths(items);
        }

        #endregion

        #region GetFileAsync

        public virtual async Task<File> GetFileAsync(string path, bool loadFileDetails, CancellationToken cancellationToken)
        {
            path = GetPath(path);
            var item = await this.StorageManager.GetFileAsync(path, loadFileDetails, cancellationToken).ConfigureAwait(false);
            item.FullName = GetStoragePath(item.StorageName);
            item.StorageName = GetStoragePath(item.StorageName);
            return item;
        }

        #endregion
        #region GetFilesAsync

        public async Task<IEnumerable<File>> GetFilesAsync(string path, string searchPattern, SearchOption searchOption, bool loadFileDetails,
            CancellationToken cancellationToken)
        {
            path = GetPath(path);
            var items = await this.StorageManager.GetFilesAsync(path, GetSearchPatterns(searchPattern), GetSearchOption(searchOption), loadFileDetails, cancellationToken).ConfigureAwait(false);
            foreach (var item in items)
            {
                item.FullName = GetStoragePath(item.StorageName);
                item.StorageName = GetStoragePath(item.StorageName);
            }
            return items;
        }

        #endregion
        #region GetFolderAsync

        public virtual async Task<Folder> GetFolderAsync(string folderName, bool loadFolderDetails, CancellationToken cancellationToken)
        {
            folderName = GetPath(folderName);
            var item = await this.StorageManager.GetFolderAsync(folderName, loadFolderDetails, cancellationToken).ConfigureAwait(false);
            item.FullName = GetStoragePath(item.StorageName);
            item.StorageName = GetStoragePath(item.StorageName);
            return item;
        }

        #endregion
        #region GetFoldersAsync

        public async Task<IEnumerable<Folder>> GetFoldersAsync(string path, string searchPattern, SearchOption searchOption, bool loadFolderDetails,
             CancellationToken cancellationToken)
        {
            path = GetPath(path);
            var items = await this.StorageManager.GetFoldersAsync(path, GetSearchPatterns(searchPattern), GetSearchOption(searchOption), loadFolderDetails, cancellationToken).ConfigureAwait(false);
            foreach (var item in items)
            {
                item.FullName = GetStoragePath(item.StorageName);
                item.StorageName = GetStoragePath(item.StorageName);
            }
            return items;
        }

        #endregion

        #region OpenFileAsync

        public virtual async Task<System.IO.Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken)
        {
            path = GetPath(path);
            return await this.StorageManager.OpenFileAsync(path, mode, access, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #region CopyFileAsync

        public async Task<bool> CopyFileAsync(string sourceFileName, string destinationFileName, bool deleteExistingFile, CancellationToken cancellationToken)
        {
            sourceFileName = GetPath(sourceFileName);
            destinationFileName = GetPath(destinationFileName);
            return await this.StorageManager.CopyFileAsync(sourceFileName, destinationFileName, deleteExistingFile, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region MoveFileAsync

        public async Task<bool> MoveFileAsync(string sourceFileName, string destinationFileName, bool deleteExistingFile, CancellationToken cancellationToken)
        {
            sourceFileName = GetPath(sourceFileName);
            destinationFileName = GetPath(destinationFileName);
            return await this.StorageManager.MoveFileAsync(sourceFileName, destinationFileName, deleteExistingFile, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region MoveFolderAsync

        public async Task<bool> MoveFolderAsync(string sourceFolderName, string destinationFolderName, bool deleteExistingFolder, CancellationToken cancellationToken)
        {
            sourceFolderName = GetPath(sourceFolderName);
            destinationFolderName = GetPath(destinationFolderName);
            return await this.StorageManager.MoveFolderAsync(sourceFolderName, destinationFolderName, deleteExistingFolder, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #region GetStoragePaths

        protected virtual List<string> GetStoragePaths(IEnumerable<string> paths)
        {
            var list = new List<string>();
            foreach (var path in paths)
                list.Add(GetStoragePath(path));
            return list;
        }

        #endregion
        #region GetSearchPattern

        protected virtual IEnumerable<string> GetSearchPatterns(string searchPattern)
        {
            return new string[] { searchPattern };
        }

        #endregion
        #region GetSearchOption

        protected virtual SearchOption GetSearchOption(SearchOption searchOption)
        {
            return searchOption;
        }

        #endregion

        #endregion
        #region Private Members

        #region InternalFolderDelimiter

        private string InternalFolderDelimiter
        {
            get
            {
                return this.StorageManager.Storage.FolderDelimiter;
            }
        }

        #endregion

        #endregion
    }
}
